/* $Id: check_passwd.c,v 1.1.2.1 2008/10/15 17:38:12 joel Exp $ */

/* Read a password, encrypt it and compare to the encrypted 
 * password in the TELNETD_PASSWD environment variable.
 * No password is required if TELNETD_PASSWD is unset
 */

/* 
 * Authorship
 * ----------
 * This software was created by
 *     Till Straumann <strauman@slac.stanford.edu>, 2003-2007
 * 	   Stanford Linear Accelerator Center, Stanford University.
 * 
 * Acknowledgement of sponsorship
 * ------------------------------
 * This software was produced by
 *     the Stanford Linear Accelerator Center, Stanford University,
 * 	   under Contract DE-AC03-76SFO0515 with the Department of Energy.
 * 
 * Government disclaimer of liability
 * ----------------------------------
 * Neither the United States nor the United States Department of Energy,
 * nor any of their employees, makes any warranty, express or implied, or
 * assumes any legal liability or responsibility for the accuracy,
 * completeness, or usefulness of any data, apparatus, product, or process
 * disclosed, or represents that its use would not infringe privately owned
 * rights.
 * 
 * Stanford disclaimer of liability
 * --------------------------------
 * Stanford University makes no representations or warranties, express or
 * implied, nor assumes any liability for the use of this software.
 * 
 * Stanford disclaimer of copyright
 * --------------------------------
 * Stanford University, owner of the copyright, hereby disclaims its
 * copyright and all other rights in this software.  Hence, anyone may
 * freely use it for any purpose without restriction.  
 * 
 * Maintenance of notices
 * ----------------------
 * In the interest of clarity regarding the origin and status of this
 * SLAC software, this and all the preceding Stanford University notices
 * are to remain affixed to any copy or derivative of this software made
 * or distributed by the recipient and are to be affixed to any copy of
 * software made or distributed by the recipient that contains a copy or
 * derivative of this software.
 * 
 * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
 */ 

#if !defined(INSIDE_TELNETD) && !defined(__rtems__)
#include <crypt.h>
#endif
#include <termios.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>

#include "passwd.h"

/* rtems has global filedescriptors but per-thread stdio streams... */
#define STDI_FD fileno(stdin)
#define MAXPASSRETRY	3

extern char *__des_crypt_r(char *, char*, char*, int);

#if !defined(INSIDE_TELNETD)
#define sockpeername(s,b,sz) (-1)
#endif

#if defined(INSIDE_TELNETD)
static
#endif
int check_passwd(char *peername)
{
  char			*pw;
  int			rval = -1, tmp, retries;
  struct termios	t,told;
  int			restore_flags = 0;
  char			buf[30], cryptbuf[21];
  char			salt[3];

  if ( !(pw=getenv("TELNETD_PASSWD")) || 0 == strlen(pw) )
#ifdef TELNETD_DEFAULT_PASSWD
    pw = TELNETD_DEFAULT_PASSWD;
#else
    return 0;
#endif

  if ( tcgetattr(STDI_FD, &t) ) {
    perror("check_passwd(): tcgetattr");
    goto done;	
  }
  told = t;
  t.c_lflag &= ~ECHO;
  t.c_lflag &= ~ICANON;
  t.c_cc[VTIME] = 255;
  t.c_cc[VMIN]  = 0;

  strncpy(salt,pw,2);
  salt[2]=0;

  if ( tcsetattr(STDI_FD, TCSANOW, &t) ) {
    perror("check_passwd(): tcsetattr");
    goto done;	
  }
  restore_flags = 1;

  /* Here we ask for the password... */
  for ( retries = MAXPASSRETRY; retries > 0; retries-- ) {
    fflush(stdin);
    fprintf(stderr,"Password:");
    fflush(stderr);
    if ( 0 == fgets(buf,sizeof(buf),stdin) ) {
      /* Here comes an ugly hack:
       * The termios driver's 'read()' handler
       * returns 0 to the c library's fgets if
       * it times out. 'fgets' interprets this
       * (correctly) as EOF, a condition we want
       * to undo since it's not really true since
       * we really have a read error (termios bug??)
       *
       * As a workaround we push something back and
       * read it again. This should simply reset the
       * EOF condition.
       */
      if (ungetc('?',stdin) >= 0)
        fgetc(stdin);
      goto done;
    }
    fputc('\n',stderr);
    tmp = strlen(buf);
    while ( tmp > 0 && ('\n' == buf[tmp-1] || '\r' == buf[tmp-1]) ) {
      buf[--tmp]=0;
    }
    if ( !strcmp(__des_crypt_r(buf, salt, cryptbuf, sizeof(cryptbuf)), pw) ) {
      rval = 0;
      break;
    }
    fprintf(stderr,"Incorrect Password.\n");
    sleep(2);
  }

  if ( 0 == retries ) {
    syslog( LOG_AUTHPRIV | LOG_WARNING,
      "telnetd: %i wrong passwords entered from %s",
      MAXPASSRETRY,
      peername ? peername : "<UNKNOWN>");
  }

done:
  /* what to do if restoring the flags fails?? */
  if (restore_flags)
    tcsetattr(STDI_FD, TCSANOW, &told);
  
  if (rval) {
    sleep(2);
  }
  return rval;
}

#if !defined(INSIDE_TELNETD) && !defined(__rtems__)
int
main(int argc, char **argv)
{
char *str, *enc=0;
int   ch;

while ( (ch=getopt(argc, argv, "g:")) > 0 ) {
  switch (ch) {
    default:
      fprintf(stderr,"Unknown option\n");
    return(1);

    case 'g':
      printf("Generated encrypted password: '%s'\n", (enc=crypt(optarg,"td")));
    break;
      
  }
}
if (argc>optind && !enc) {
  enc=argv[optind];
}
if (enc) {
  str = malloc(strlen(enc) + 30);
  sprintf(str,"TELNETD_PASSWD=%s",enc);
  putenv(str);
}
if (check_passwd(-1)) {
  fprintf(stderr,"check_passwd() failed\n");
}
return 0;
}

#endif
